﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Quix;

namespace Quix.ToggleFeature
{
    class Program
    {
        /// <summary>
        /// This utility is used to disable and re-enable Features in a SharePoint farm.
        /// It will determine the scope of the Feature and then specifically target that level of objects in the toggle process
        /// i.e. if a Site level Feature is targeted, it will only itterate site collections for the toggle and ignore sub webs.
        /// </summary>
        /// <param name="args">-id [GUIDofFeature]</param>
        [System.STAThreadAttribute()]
        [PreEmptive.Attributes.Setup(CustomEndpoint = "so-s.info/PreEmptive.Web.Services.Messaging/MessagingServiceV2.asmx")]
        [PreEmptive.Attributes.Teardown()]
        [PreEmptive.Attributes.Feature("Quix.ToggleFeature")]
        static void Main(string[] args)
        {
            if (args.Count() == 0)
            {
                //Write instructions if no arguments exist.
                PrintUsage();
            }
            else
            {
                try
                {
                    //Get a hash table of arguments.
                    System.Collections.Hashtable tbl = Core.HashArgs(args);
                    if (!tbl.ContainsKey("-id"))
                    {
                        //Write instructions if the -id argument was not supplied.
                        PrintUsage();
                    }
                    else
                    {
                        if (!tbl.ContainsKey("-mode"))
                        {
                            tbl.Add("-mode", "default");
                        }
                        StartProcessing();
                        //Create the log file.
                        System.IO.StreamWriter sw = new System.IO.StreamWriter(Core.ConvertArgsToLogFile(tbl["-id"].ToString()));
                        List<string> lstTargets = new List<string>();
                        //Get a reference to the local farm's Feature definitions.
                        SPFarm frm = SPFarm.Local; 
                        SPFeatureDefinition sfd = new SPFeatureDefinition();
                        //Locate the target Feature.
                        foreach (SPFeatureDefinition f in frm.FeatureDefinitions)
                        {
                            if (f.Id.ToString().ToLower() == tbl["-id"].ToString().ToLower())
                            {
                                sfd = f;
                                Console.ForegroundColor = ConsoleColor.White;
                                if (tbl["-mode"].ToString().ToLower() == "disableonly")
                                {
                                    Core.Log((string)"Disabling Feature [" + f.DisplayName + "] with ID [" + tbl["-id"].ToString().ToLower() + "]", sw, ConsoleColor.Green);
                                }
                                else
                                {
                                    if (tbl["-mode"].ToString().ToLower() == "enableonly")
                                    {
                                        Core.Log((string)"Enabling Feature [" + f.DisplayName + "] with ID [" + tbl["-id"].ToString().ToLower() + "]", sw, ConsoleColor.Green);
                                    }
                                    else
                                    {
                                        Core.Log((string)"Toggling Feature [" + f.DisplayName + "] with ID [" + tbl["-id"].ToString().ToLower() + "]", sw, ConsoleColor.Green);
                                    }
                                }
                                Core.Log((string)"=".PadRight(78, '='), sw, ConsoleColor.Green);
                                break;
                            }
                        }
                        //Convert the target Feature ID to a GUID.
                        Guid g = new Guid(tbl["-id"].ToString().ToLower()); 
                        //Get a list of all the web applications.
                        SPWebApplicationCollection wac = SPWebService.ContentService.WebApplications;
                        //Check the scope of the target Feature and process accordingly.
                        switch (sfd.Scope)
                        {
                            //If it's a farm scoped Feature we simply process the farm.
                            case SPFeatureScope.Farm:
                                Core.Log("Processing Farm: " + frm.DisplayName, sw); 
                                switch (tbl["-mode"].ToString().ToLower())
                                {
                                    case "enableonly":
                                        frm.FeatureDefinitions.Add(sfd);
                                        break;
                                    case "disableonly":
                                        frm.FeatureDefinitions.Remove(g, true);
                                        break;
                                    default:
                                        frm.FeatureDefinitions.Remove(g, true);
                                        frm.FeatureDefinitions.Add(sfd);
                                        break;
                                }
                                break;
                            //If it's a web application scoped Feature, we process each web app.
                            case SPFeatureScope.WebApplication:
                                foreach (SPWebApplication app in wac)
                                {
                                    Core.Log("Processing WebApp: " + app.DisplayName, sw);
                                    //Have to have a try/catch here in case the executing user doesn't have permissions on one or more web apps.
                                    try
                                    {
                                        foreach (SPFeature ftr in app.Features)
                                        {
                                            if (ftr.DefinitionId == g)
                                            {
                                                //This try/catch ensures that the app notifies the user of any failure, but still continues.
                                                try
                                                {
                                                    switch (tbl["-mode"].ToString().ToLower())
                                                    {
                                                        case "enableonly":
                                                            app.Features.Add(g, true);
                                                            break;
                                                        case "disableonly":
                                                            app.Features.Remove(g, true); 
                                                            break;
                                                        default:
                                                            app.Features.Remove(g, true); 
                                                            app.Features.Add(g, true);
                                                            break;
                                                    }
                                                    Core.Log("  Toggled OK!", sw);
                                                }
                                                catch (Exception ex)
                                                {
                                                    Core.Log(ex.Message, sw, ConsoleColor.Red);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Core.Log(ex.Message, sw, ConsoleColor.Red); 
                                    }
                                }
                                break;
                            //If it's a site scoped Feature, we have to process each site collection in each web app.
                            case SPFeatureScope.Site: 
                                foreach (SPWebApplication app in wac)
                                {
                                    Core.Log("Itterating WebApp: " + app.DisplayName, sw); 
                                    //Catch web app permissions issues.
                                    try
                                    {
                                        foreach (SPSite sps in app.Sites)
                                        {
                                            Core.Log("  Processing Site: " + sps.Url, sw); 
                                            //Catch site collection permissions issues.
                                            try
                                            {
                                                foreach (SPFeature ftr in sps.Features)
                                                {
                                                    if (ftr.DefinitionId == g)
                                                    {
                                                        //Handle deactivation/activation problems.
                                                        try
                                                        {
                                                            switch (tbl["-mode"].ToString().ToLower())
                                                            {
                                                                case "enableonly":
                                                                    sps.Features.Add(g, true);
                                                                    break;
                                                                case "disableonly":
                                                                    sps.Features.Remove(g, true); 
                                                                    break;
                                                                default:
                                                                    sps.Features.Remove(g, true); 
                                                                    sps.Features.Add(g, true);
                                                                    break;
                                                            }
                                                            Core.Log("    Toggled OK!", sw);
                                                        }
                                                        catch (Exception ex)
                                                        { 
                                                            Core.Log(ex.Message, sw, ConsoleColor.Red); 
                                                        }
                                                        break;
                                                    }
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                Core.Log(ex.Message, sw, ConsoleColor.Red);
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    { 
                                        Core.Log(ex.Message, sw, ConsoleColor.Red); 
                                    }
                                }
                                break;
                            //If it's a web scoped Feature, we have to process each sub web in each site collection in each web app.
                            case SPFeatureScope.Web: 
                                foreach (SPWebApplication app in wac)
                                {
                                    Core.Log("Itterating WebApp: " + app.DisplayName, sw);
                                    //Catch web app permissions issues.
                                    try
                                    {
                                        foreach (SPSite sps in app.Sites)
                                        {
                                            Core.Log("  Itterating Site: " + sps.Url, sw);
                                            //Catch site collection permissions issues.
                                            try
                                            {
                                                foreach (SPWeb spw in sps.AllWebs)
                                                {
                                                    Core.Log("    Processing Web: " + spw.Url, sw);
                                                    //Catch web permissions issues.
                                                    try
                                                    {
                                                        foreach (SPFeature ftr in spw.Features)
                                                        {
                                                            if (ftr.DefinitionId.ToString() == g.ToString())
                                                            {
                                                                //Handle deactivation/activation problems.
                                                                try
                                                                {
                                                                    switch (tbl["-mode"].ToString().ToLower())
                                                                    {
                                                                        case "enableonly":
                                                                            spw.Features.Add(g, true);
                                                                            break;
                                                                        case "disableonly":
                                                                            spw.Features.Remove(g, true);
                                                                            break;
                                                                        default:
                                                                            spw.Features.Remove(g, true);
                                                                            spw.Features.Add(g, true);
                                                                            break;
                                                                    }
                                                                    Core.Log("      Toggled OK!", sw);
                                                                }
                                                                catch (Exception ex)
                                                                {
                                                                    Core.Log(ex.Message, sw, ConsoleColor.Red);
                                                                } 
                                                                break;
                                                            }
                                                        }
                                                    }
                                                    catch (Exception ex)
                                                    {
                                                        Core.Log(ex.Message, sw, ConsoleColor.Red);
                                                    }
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                Core.Log(ex.Message, sw, ConsoleColor.Red);
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Core.Log(ex.Message, sw, ConsoleColor.Red);
                                    }
                                } break;
                        } 
                        sw.Flush();
                        StopProcessing();
                    }
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(ex.Message);
                }
            }
        }

        static void PrintUsage()
        {
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(@"Command line error.");
            Console.WriteLine(@"");
            Console.WriteLine(@"  Usage:  " + Environment.GetCommandLineArgs()[0] + " -id <GUID>");
            Console.WriteLine(@"    -id <GUID> - GUID of the target Feature to toggle e.g. 27c49ad6-65d9-40b8-85d4-25e0ea4e8444");
            Console.WriteLine(@"    {-mode} <MODE> - OPTIONAL - Any of these modes ""disableonly"", ""enableonly"" or ""toggle"".  The default value it ""toggle""");
            Console.WriteLine(@"    Example usage would be:");
            Console.WriteLine(@"      " + Environment.GetCommandLineArgs()[0] + " -id 27c49ad6-65d9-40b8-85d4-25e0ea4e8444");
            Console.WriteLine(@"       or");
            Console.WriteLine(@"      " + Environment.GetCommandLineArgs()[0] + " -id 27c49ad6-65d9-40b8-85d4-25e0ea4e8444 -mode disableonly");
        }

        [PreEmptive.Attributes.Feature("Quix.ToggleFeature", EventType = PreEmptive.Attributes.FeatureEventTypes.Start)]
        static void StartProcessing()
        {
        }

        [PreEmptive.Attributes.Feature("Quix.ToggleFeature", EventType = PreEmptive.Attributes.FeatureEventTypes.Stop)]
        static void StopProcessing()
        {
        }
    }
}
